/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: StringView.h
 *
 * Purpose: This section describes the ara::core::StringView type that 
 *          constitutes a readonly view over a contiguous sequence of 
 *          characters, the storage of which is owned by another object
 *
 * Developer:
 *   wen.gu , 2020-04-08
 *
 * TODO:
 *
 ***************************************************************************/

#ifndef _ARA_CORE_STRING_VIEW_H__
#define _ARA_CORE_STRING_VIEW_H__
/******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include <cstdint>
#include <iostream>
#include <assert.h>
#include <limits>
#include <algorithm>

#include "ara/core/reverse_iterator.h"

/******************************************************************************
 **    MACROS
 ******************************************************************************/

#if __cplusplus >= 201402L
#define NonConstConstexpr constexpr
#else
#define NonConstConstexpr
#endif


/******************************************************************************
 **    TYPE DEFINITIONS
 ******************************************************************************/
namespace ara
{
namespace core
{
/******************************************************************************
 **    CLASSES/FUNCTIONS DEFINITIONS
 ******************************************************************************/

/** class template BasicStringView */

template<class charT, class traits = std::char_traits<charT>>
class BasicStringView {
public:
    // types
    using traits_type = traits;
    using value_type = charT;
    using pointer = value_type*;
    using const_pointer = const value_type*;
    using reference = value_type&;
    using const_reference = const value_type&;
    using const_iterator = const_pointer; // see 24.4.2.2
    using iterator = const_iterator;
    using const_reverse_iterator = ReverseIterator<const_iterator>;
    using reverse_iterator = const_reverse_iterator;
    using size_type = size_t;
    using difference_type = ptrdiff_t;
    static constexpr size_type npos = size_type(-1);

public:
    // 24.4.2.1, construction and assignment
    constexpr BasicStringView() noexcept
        :size_(0),
         data_(nullptr)
    {
        /** todo something */
    }

    constexpr BasicStringView(const BasicStringView&) noexcept = default;
    NonConstConstexpr BasicStringView& operator=(const BasicStringView&) noexcept = default;

    constexpr BasicStringView(const charT* str)
        :size_{str == nullptr ? 0 : traits_type::length(str)},
         data_{str}
    {
        /** todo something */
    }

    constexpr BasicStringView(const charT* str, size_type len)
        :size_(len),
         data_(str)
    {
        /** todo something */
    }

    // 24.4.2.2, iterator support
    constexpr const_iterator begin() const noexcept
    {
        return this->data_;
    }

    constexpr const_iterator end() const noexcept
    {
        return this->data_ + this->size_;
    }

    constexpr const_iterator cbegin() const noexcept
    {
        return this->data_;
    }

    constexpr const_iterator cend() const noexcept
    {
        return this->data_ + this->size_;
    }

    constexpr const_reverse_iterator rbegin() const noexcept
    {
        return const_reverse_iterator(this->end());
    }

    constexpr const_reverse_iterator rend() const noexcept
    {
        return const_reverse_iterator(this->begin());
    }

    constexpr const_reverse_iterator crbegin() const noexcept
    {
        return const_reverse_iterator(this->end());
    }

    constexpr const_reverse_iterator crend() const noexcept
    {
        return const_reverse_iterator(this->begin());
    }

    // 24.4.2.3, capacity
    constexpr size_type size() const noexcept
    {
        return this->size_;
    }

    constexpr size_type length() const noexcept
    {
        return size_;
    }

    constexpr size_type max_size() const noexcept
    {
        return (npos - sizeof(size_type) - sizeof(void*)) / sizeof(value_type) / 4;
    }

    constexpr bool empty() const noexcept
    {
        return this->size_ == 0;
    }

    // 24.4.2.4, element access
    constexpr const_reference operator[](size_type pos) const
    {
        // TODO: Assert to restore in a way compatible with the constexpr.
        //assert(pos < this->size_);
        return *(this->data_ + pos);
    }

    constexpr const_reference at(size_type pos) const
    {
        if (pos >= size_)
        {
            throw std::out_of_range("pos out of size range");
            //__throw_out_of_range_fmt(__N("BasicStringView::at: pos "
            //               "(which is %zu) >= this->size() "
            //               "(which is %zu)"), pos, this->size());
        }
        return *(this->_M_str + pos);
    }

    constexpr const_reference front() const
    {
        // TODO: Assert to restore in a way compatible with the constexpr.
        // assert(this->size_ > 0);
        return *this->data_;
    }

    constexpr const_reference back() const
    {
        // TODO: Assert to restore in a way compatible with the constexpr.
        // assert(this->size_ > 0);
        return *(this->data_ + this->size_ - 1);
    }

    constexpr const_pointer data() const noexcept
    {
        return this->data_;
    }

    // 24.4.2.5, modifiers
    NonConstConstexpr void remove_prefix(size_type n)
    {
        assert(this->size_ >= n);
        this->data_ += n;
        this->size_ -= n;
    }

    NonConstConstexpr void remove_suffix(size_type n)
    {
        this->size_ -= n;
    }

    NonConstConstexpr void swap(BasicStringView& s) noexcept
    {
        auto tmp = *this;
        *this = s;
        s = tmp;
    }

    // 24.4.2.6, string operations
    size_type copy(charT* s, size_type n, size_type pos = 0) const
    {
        assert(s != nullptr);
        assert(n > 0);
        pos = _M_check(pos, "BasicStringView::copy");
        const size_type __rlen = std::min(n, size_ - pos);
        for (auto __begin = this->data_ + pos, __end = __begin + __rlen; __begin != __end;)
        {
          *s++ = *__begin++;
        }
        return __rlen;
    }

    constexpr BasicStringView substr(size_type pos = 0, size_type n = npos) const
    {
        pos = _M_check(pos, "BasicStringView::substr");
        const size_type __rlen = std::min(n, size_ - pos);
        return BasicStringView{data_ + pos, __rlen};
    }

    constexpr int compare(BasicStringView s) const noexcept
    {
        const size_type __rlen = std::min(this->size_, s.size_);
        int __ret = traits_type::compare(this->data_, s.data_, __rlen);
        if (__ret == 0)
        {
            __ret = _S_compare(this->size_, s.size_);
        }

        return __ret;
    }

    constexpr int compare(size_type pos1, size_type n1, BasicStringView s) const
    {
        return this->substr(pos1, n1).compare(s);
    }

    constexpr int compare(size_type pos1, size_type n1, BasicStringView s, size_type pos2, size_type n2) const
    {
        return this->substr(pos1, n1).compare(s.substr(pos2, n2));
    }

    constexpr int compare(const charT* s) const
    {
        return this->compare(BasicStringView{s});
    }

    constexpr int compare(size_type pos1, size_type n1, const charT* s) const
    {
        return this->substr(pos1, n1).compare(BasicStringView{s});
    }

    constexpr int compare(size_type pos1, size_type n1, const charT* s,  size_type n2) const
    {
        return this->substr(pos1, n1).compare(BasicStringView{s, n2});
    }

    constexpr size_type find(BasicStringView s, size_type pos = 0) const noexcept
    {
        return this->find(s._M_str, pos, s.size_);
    }

    constexpr size_type find(charT c, size_type pos = 0) const noexcept;
    constexpr size_type find(const charT* s, size_type pos, size_type n) const;

    constexpr size_type find(const charT* s, size_type pos = 0) const
    {
        return this->find(s, pos, traits_type::length(s));
    }

    constexpr size_type rfind(BasicStringView s, size_type pos = npos) const noexcept
    {
        return this->rfind(s.data_, pos, s.size_);
    }

    constexpr size_type rfind(charT c, size_type pos = npos) const noexcept;

    constexpr size_type rfind(const charT* s, size_type pos, size_type n) const;

    constexpr size_type rfind(const charT* s, size_type pos = npos) const
    {
        return this->rfind(s, pos, traits_type::length(s));
    }

    constexpr size_type find_first_of(BasicStringView s, size_type pos = 0) const noexcept
    {
        return this->find_first_of(s.data_, pos, s.size_);
    }

    constexpr size_type find_first_of(charT c, size_type pos = 0) const noexcept
    {
        return this->find(c, pos);
    }

    constexpr size_type find_first_of(const charT* s, size_type pos, size_type n) const;

    constexpr size_type find_first_of(const charT* s, size_type pos = 0) const
    {
        return this->find_first_of(s, pos, traits_type::length(s));
    }

    constexpr size_type find_last_of(BasicStringView s, size_type pos = npos) const noexcept
    {
        return this->find_last_of(s.data_, pos, s.size_);
    }

    constexpr size_type find_last_of(charT c, size_type pos = npos) const noexcept
    {
        return this->rfind(c, pos);
    }

    constexpr size_type find_last_of(const charT* s, size_type pos, size_type n) const;

    constexpr size_type find_last_of(const charT* s, size_type pos = npos) const
    {
        return this->find_last_of(s, pos, traits_type::length(s));
    }

    constexpr size_type find_first_not_of(BasicStringView s, size_type pos = 0) const noexcept
    {
        return this->find_first_not_of(s.data_, pos, s.size_);
    }

    constexpr size_type find_first_not_of(charT c, size_type pos = 0) const noexcept;

    constexpr size_type find_first_not_of(const charT* s, size_type pos, size_type n) const;

    constexpr size_type find_first_not_of(const charT* s, size_type pos = 0) const
    {
        return this->find_first_not_of(s, pos, traits_type::length(s));
    }

    constexpr size_type find_last_not_of(BasicStringView s, size_type pos = npos) const noexcept
    {
        return this->find_last_not_of(s.data_, pos, s.size_);
    }

    constexpr size_type find_last_not_of(charT c, size_type pos = npos) const noexcept;
    constexpr size_type find_last_not_of(const charT* s, size_type pos, size_type n) const;

    constexpr size_type find_last_not_of(const charT* s, size_type pos = npos) const
    {
        return this->find_last_not_of(s, pos,  traits_type::length(s));
    }

private:
    constexpr size_type _M_check(size_type __pos, const char* __s) const noexcept(false)
    {
        if (__pos > this->size())
        {
            throw std::out_of_range(__s);
            //__throw_out_of_range_fmt(__N("%s: __pos (which is %zu) > "
            //     "this->size() (which is %zu)"),
            // __s, __pos, this->size());
        }
            return __pos;
    }

    // NB: _M_limit doesn't check for a bad __pos value.
    constexpr size_type _M_limit(size_type __pos, size_type __off) const noexcept
    {
        const bool __testoff =  __off < this->size() - __pos;
        return __testoff ? __off : this->size() - __pos;
    }

    static constexpr int _S_compare(size_type __n1, size_type __n2) noexcept
    {
        const difference_type __diff = __n1 - __n2;
        if (__diff > std::numeric_limits<int>::max())
        {
            return std::numeric_limits<int>::max();
        }

        if (__diff < std::numeric_limits<int>::min())
        {
            return std::numeric_limits<int>::min();
        }

        return static_cast<int>(__diff);
    }
private:
    size_type size_; // exposition only
    const_pointer data_; // exposition only

};


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** non-member comparison functions */
template<class charT, class traits>
constexpr bool operator==(BasicStringView<charT, traits> x, BasicStringView<charT, traits> y) noexcept
{
    return x.size() == y.size() && x.compare(y) == 0;
}

template<class charT, class traits>
constexpr bool operator!=(BasicStringView<charT, traits> x, BasicStringView<charT, traits> y) noexcept
{
    return !(x == y);
}

template<class charT, class traits>
constexpr bool operator< (BasicStringView<charT, traits> x, BasicStringView<charT, traits> y) noexcept
{
    return x.compare(y) < 0;
}

template<class charT, class traits>
constexpr bool operator> (BasicStringView<charT, traits> x, BasicStringView<charT, traits> y) noexcept
{
    return x.compare(y) > 0;
}

template<class charT, class traits>
constexpr bool operator<=(BasicStringView<charT, traits> x, BasicStringView<charT, traits> y) noexcept
{
    return x.compare(y) <= 0;
}

template<class charT, class traits>
constexpr bool operator>=(BasicStringView<charT, traits> x, BasicStringView<charT, traits> y) noexcept
{
    return x.compare(y) >= 0;
}

/** inserters and extractors */
namespace internal_detial {
template <class _Elem, class _Traits, class _SizeT>
std::basic_ostream<_Elem, _Traits>& My_Insert_String(std::basic_ostream<_Elem, _Traits>& _Ostr, const _Elem* const _Data, const _SizeT _Size)
{
    // insert a character-type sequence into _Ostr as if through a basic_string copy
    using _Ostr_t                    = std::basic_ostream<_Elem, _Traits>;
    typename _Ostr_t::iostate _State = _Ostr_t::goodbit;

    _SizeT _Pad;
    if (_Ostr.width() <= 0 || static_cast<_SizeT>(_Ostr.width()) <= _Size)
    {
        _Pad = 0;
    } else
    {
        _Pad = static_cast<_SizeT>(_Ostr.width()) - _Size;
    }

    const typename _Ostr_t::sentry _Ok(_Ostr);

    if (!_Ok)
    {
        _State |= _Ostr_t::badbit;
    }
    else
    { // state okay, insert characters
        try
        {
            if ((_Ostr.flags() & _Ostr_t::adjustfield) != _Ostr_t::left)
            {
                for (; 0 < _Pad; --_Pad)
                { // pad on left
                    if (_Traits::eq_int_type(_Traits::eof(), _Ostr.rdbuf()->sputc(_Ostr.fill())))
                    {
                        _State |= _Ostr_t::badbit; // insertion failed, quit
                        break;
                    }
                }
            }

            if ((_State == _Ostr_t::goodbit)  &&
                 (_Ostr.rdbuf()->sputn(_Data, static_cast<std::streamsize>(_Size)) != static_cast<std::streamsize>(_Size)))
            {
                _State |= _Ostr_t::badbit;
            }
            else
            {
                for (; 0 < _Pad; --_Pad)
                { // pad on right
                    if (_Traits::eq_int_type(_Traits::eof(), _Ostr.rdbuf()->sputc(_Ostr.fill())))
                    {
                        _State |= _Ostr_t::badbit; // insertion failed, quit
                        break;
                    }
                }
            }

            _Ostr.width(0);
        }
        catch(...)
        {
            _Ostr.setstate(_Ostr_t::badbit, true);
        }
    }

    _Ostr.setstate(_State);
    return _Ostr;
}
}

template<class charT, class traits>
std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, BasicStringView<charT, traits> str)
{
    return internal_detial::My_Insert_String(os, str.data(), str.size());
}


/** BasicStringView typedef names */
using StringView = BasicStringView<char>;

/** hash support */
template<class T> struct hash;
template<> struct hash<StringView>;

/** suffix for BasicStringView literals */
constexpr StringView operator"" _sv(const char* str, size_t len) noexcept;


} /** namespace core */
} /** namespace ara */
#endif /** !_ARA_CORE_STRING_VIEW_H__ */

